/*
============================================================================
 Name        : dd_queue.c
 Author      : Jian (raphyer@hotmail.com)
 Version     :
 Copyright   : GPL
 Description : dd_queue.c - source file
============================================================================
*/

/* INCLUDE FILES */
#include "dd_queue.h"


typedef struct __dd_queue_node  dd_queue_node;

struct __dd_queue_node{
    dd_queue_value* value ;
    dd_queue_node*  next ;
};

static dd_queue_node* _create_node(dd_queue_value* value) 
{
    dd_queue_node* node = (dd_queue_node*)DD_MALLOC(sizeof(dd_queue_node)) ;
    node->value = value ;
    node->next  = DD_NULL ;
    return node ;
}

static void _destroy_node(dd_queue_node* node, void (*free_fun)(void*)) 
{
    __DD_ASSERT_DEFENSE(node, return) ;

    if (free_fun) 
        free_fun(node->value) ;
    DD_FREE(node) ;
}

dd_queue* dd_queue_create() 
{
    dd_queue* queue = (dd_queue*)DD_MALLOC(sizeof(dd_queue)) ;
    queue->head = DD_NULL ;
    queue->tail = DD_NULL ;
    return queue ;
}

void dd_queue_destroy(dd_queue* queue, void (*free_fun)(void*)) 
{
    __DD_ASSERT_DEFENSE(queue, return) ;

    dd_queue_reset(queue, free_fun) ;
    DD_FREE(queue) ;
}

void dd_queue_reset(dd_queue* queue, void (*free_fun)(void*)) 
{
    __DD_ASSERT_DEFENSE(queue, return) ;

    while(queue->head) {
        dd_queue_node* node = queue->head ;
        queue->head = queue->head->next ;
        _destroy_node(node, free_fun) ;
    }

    queue->head = DD_NULL ;
    queue->tail = DD_NULL ;
}

void dd_queue_add(dd_queue* queue, dd_queue_value* value) 
{
    __DD_ASSERT_DEFENSE(queue, return) ;

    if (!queue->head) {
        queue->head = _create_node(value) ;
        queue->tail = queue->head ;
    }
    else {
        queue->tail->next = _create_node(value) ;
        queue->tail = queue->tail->next ;
    }
}

dd_queue_value* dd_queue_remove(dd_queue* queue) 
{
    dd_queue_value* ret = DD_NULL ;
    dd_queue_node* tmp  = DD_NULL ;

    __DD_ASSERT_DEFENSE(queue && !dd_queue_is_empty(queue), return DD_NULL) ;

    ret = queue->head->value ;
    
    tmp = queue->head ;

    queue->head = queue->head->next ;

    if (queue->head == DD_NULL) {
        __DD_ASSERT(tmp == queue->tail) ;
        queue->tail = DD_NULL ;
    }
     _destroy_node(tmp, DD_NULL) ;

    return ret ;
}


DD_BOOL dd_queue_is_empty(const dd_queue* queue) 
{
    __DD_ASSERT_DEFENSE(queue, return DD_FALSE) ;
    return queue->head == DD_NULL ;
}


#ifdef __DD_UNITTEST

#include "dd_vector.h"

#define __TEST_TIMES		10000

DD_BOOL __ut_check_queue(const dd_queue* queue, DD_INT count)
{
    DD_INT que_count = 0 ;
    dd_queue_node* node = queue->head ; 
    
    while(node != queue->tail) {
        que_count++ ;
        node = node->next ;
    }
    que_count++ ;

    if (que_count != count) {
        DD_LOG("__ut_check_queue check count error count = %d, que_count = %d", count, que_count) ;
        return DD_FALSE ;
    }

    return DD_TRUE ;
}

DD_BOOL dd_queue_test() 
{
    dd_queue* test_queue = dd_queue_create() ;
    dd_vector* test_vec  = dd_vector_create(__TEST_TIMES) ;

    DD_INT i = 0 ;
    DD_INT loop = 2 ;

    while( loop > 0 ) {
        i = 0 ;
        for (; i < __TEST_TIMES; i++) {
            dd_queue_add(test_queue, (void*)i) ;
            dd_vector_append(test_vec, (void*)i) ;

            if (!__ut_check_queue(test_queue, i+1))
                return DD_FALSE ;
        }
        
        i =  0;
        for (; i < __TEST_TIMES; i++) {
            
            if (!__ut_check_queue(test_queue, dd_vector_count(test_vec)))
                return DD_FALSE ;

            if ((DD_INT)dd_queue_remove(test_queue) != (DD_INT)dd_vector_delete_by_index(test_vec, 0)) {
                DD_LOG("dd_queue_remove ERROR") ;
                return DD_FALSE ;
            }
        }
        
        if (!dd_queue_is_empty(test_queue)) {
            DD_LOG("dd_queue_is_empty ERROR") ;
            return DD_FALSE ;
        }

        dd_vector_reset(test_vec, DD_NULL) ;
        loop-- ;
    }
    
    for (; i < __TEST_TIMES; i++) {
        dd_queue_add(test_queue, DD_MALLOC(4)) ;
     }

    dd_queue_reset(test_queue, DD_FREE) ;
    
    dd_vector_destroy(test_vec, DD_NULL) ;
    dd_queue_destroy(test_queue, DD_FREE);

    return DD_TRUE ;
}

#endif	/* __DD_UNITTEST */


/* End of File */
